package com.ctu.gzero.elements
{
	import com.ctu.gzero.compose.TxLine;
	import com.ctu.gzero.compose.TxLineSegment;
	import com.ctu.gzero.formats.ICharacterFormat;
	
	import flash.text.engine.ContentElement;
	import flash.text.engine.GroupElement;
	import flash.text.engine.TextBlock;
	import flash.text.engine.TextElement;
	import flash.text.engine.TextLine;
	
	import mx.collections.ArrayCollection;
	
	public class ParaElement
	{
		private var _dom:DOMElement;
		[ArrayElementType("com.ctu.gzero.elements.RunElement")]
		private var _runList:ArrayCollection;
		private var _textBlock:TextBlock;
		private var _txLineList:ArrayCollection;
		private var _wrapObjectList:ArrayCollection;
		private var _id:String;
		protected var _absoluteStart:int;
		protected var _textLength:int = 0;
		private var _format:ICharacterFormat;
		
		public function ParaElement(id:String=null)
		{
			_id = id;
			_runList = new ArrayCollection();
			_txLineList = new ArrayCollection();
			_wrapObjectList = new ArrayCollection();
		}
		
		public function createContent():void
		{
			var runListLength:int = _runList.length;
			var groupElement:GroupElement = new GroupElement();
			var groupVector:Vector.<ContentElement> = new Vector.<ContentElement>();
			
			for(var i:int=0; i<runListLength; i++)
			{
				var run:RunElement = getRun(i);
				var textRun:TxtRun = null;
				if(run.type == "TxtRun")
				{
					textRun = run as TxtRun;
		            var te:TextElement = new TextElement(textRun.text, textRun.elementFormat);
		            textRun.textElement = te;
		            
		            groupVector.push(te);
				} 
				else if(run.type == "ObjectRun") 
				{
					_wrapObjectList.addItem(run as ObjectRun);
				}
			}
			
			groupElement.setElements(groupVector);
			
			_textBlock = new TextBlock();
			_textBlock.content = groupElement;
		}
		
		public function getTextLines():Array
		{
			var textLineList:Array = new Array();
			
			for(var i:int=0; i<_txLineList.length; i++)
			{
				var txLine:TxLine = _txLineList.getItemAt(i) as TxLine;
				for(var j:int=0; j<txLine.segments.length; j++)
				{
					var txLineSegment:TxLineSegment = txLine.segments.getItemAt(j) as TxLineSegment;
					textLineList.push(txLineSegment.textLine);
				}
			}
			
			return textLineList;
		}
		
		public function getText():String
		{
			var _text:String = "";
			var _runListLength:int = _runList.length;
			var run:RunElement;
			var txtRun:TxtRun;
			
			for(var i:int=0; i<_runListLength; i++)
			{
				run = getRun(i);
				if(run.type == "TxtRun")
				{
					txtRun = run as TxtRun;
					_text = _text.concat(txtRun.text);
				}
			}
			
			return _text;
		}
		
		public function getCharAtPosition(relativePos:int):String
		{
			var charStr:String = "";
			var _text:String = getText();
			charStr = _text.substr(relativePos, 1);
			return charStr;
		}
		
		public function getCharCodeAtPosition(relativePos:int):int
		{
			var charStr:String = "";
			var _text:String = getText();
			charStr = _text.substr(relativePos, 1);
			return charStr.charCodeAt(0);
		}
		
		public function get textLength():int
		{
			return _textLength;	
		}
		
		public function get id():String
		{
			return _id;
		}
		
		public function set id(val:String):void
		{
			_id = val;
		}
		
		public function get absoluteStart():int
		{
			return _absoluteStart;
		}
		
		public function set absoluteStart(val:int):void
		{
			_absoluteStart = val;
		}
		
		public function set dom(val:DOMElement):void
		{
			_dom = val;
			for(var i:int=0; i<_runList.length; i++)
			{
				var run:RunElement = getRun(i);
				run.dom = _dom;
			}
		}
		
		public function get dom():DOMElement
		{
			return _dom;
		}
		
		public function set textBlock(val:TextBlock):void
		{
			_textBlock = val;
		}
		
		public function get textBlock():TextBlock
		{
			return _textBlock;
		}
		
		public function set runList(val:ArrayCollection):void
		{
			_runList = val;
		}
		
		public function get runList():ArrayCollection
		{
			return _runList;
		}
		
		public function set txLineList(val:ArrayCollection):void
		{
			_txLineList = val;
		}
		
		public function get txLineList():ArrayCollection
		{
			return _txLineList;
		}
		
		public function set wrapObjectList(val:ArrayCollection):void
		{
			_wrapObjectList = val;
		}
		
		public function get wrapObjectList():ArrayCollection
		{
			return _wrapObjectList;
		}
		
		public function addRun(run:RunElement):void
		{
			run.dom = _dom;
			run.para = this;
			_runList.addItem(run);
		}
		
		public function getRun(idx:int):RunElement
		{
			return _runList.getItemAt(idx) as RunElement;
		}
		
		public function removeRun(idx:int):void
		{
			_runList.removeItemAt(idx);
		}
		
		public function normalize():void
		{			
			var length:int = 0;
			var run:RunElement;
			var txtRun:TxtRun;
			var _runListLength:int = 0;
			var _lastTxtRunIndex:int = -1;
			_runListLength = _runList.length;
			
			for(var i:int=0; i<_runListLength; i++)
			{
				run = getRun(i);
				if(run.type == "TxtRun")
					_lastTxtRunIndex = i;
			}
			
			for(var j:int=0; j<_runListLength; j++)
			{
				run = getRun(j);
				if(_lastTxtRunIndex == j)
				{
					txtRun = run as TxtRun;
					//append paragraph seperator to the last text run
					txtRun.normalize();
					if(txtRun.text.charCodeAt(txtRun.text.length-1) != 0x2029)
						txtRun.replaceText(txtRun.textLength,txtRun.textLength, String.fromCharCode(0x2029));
				}
				
				run.absoluteStart = length + _absoluteStart;
				run.relativeStart = length;
				run.normalize();
				length += run.textLength;
			}
			
			// in case of empty paragraph or with no text run, add empty text run with 0x2029
			if(_runListLength == 0 || _lastTxtRunIndex == -1)
			{
				txtRun = new TxtRun(String.fromCharCode(0x2029));
				addRun(txtRun);
				txtRun.absoluteStart = length + _absoluteStart;
				txtRun.relativeStart = length;
				txtRun.normalize();
				length += txtRun.textLength;
			}
			
			_textLength = length;
		}		 
		
		public function getRunIndex(run:RunElement):int
		{
			return _runList.getItemIndex(run);
		}
		
		public function findTextLeaf(relativePosition:int):RunElement
		{
			var txtRun:TxtRun = null;
			for(var i:int=0; i<_runList.length; i++)
			{
				if(getRun(i).type == "TxtRun")
				{
					txtRun = getRun(i) as TxtRun;
					if(relativePosition >= txtRun.relativeStart && relativePosition <= txtRun.relativeStart + txtRun.textLength -1)
					{
						break;
					}
				}
			}
			
			return txtRun;
		}
		
		public function getNextPara():ParaElement
		{
			var idx:int = _dom.getParaIndex(this);
			var nextPara:ParaElement = null;
			
			if(idx < _dom.paraList.length-1)
				nextPara = _dom.getPara(idx+1);
			
			return nextPara;	
		}
		
		public function getPreviousPara():ParaElement
		{
			var idx:int = _dom.getParaIndex(this);
			var previousPara:ParaElement = null;
			
			if(idx > 0)
				previousPara = _dom.getPara(idx-1);
			
			return previousPara;	
		}
		
		public function getTxLineAtCharIndex(relativeIndex:int):TxLine
		{
			var txLine:TxLine = null;
			var status:Boolean = false;
			
			for(var i:int=0; i<_txLineList.length; i++)
			{
				txLine = _txLineList.getItemAt(i) as TxLine;
				if(relativeIndex >= txLine.paraBeginIndex && relativeIndex <= txLine.paraBeginIndex + txLine.textLength -1)
				{
					status = true;
					break;
				}
			}
			
			if(status)
				return txLine;
			else 
				return null;
		}
		
		public function firstTxLine():TxLine
		{
			var txLine:TxLine =  null;
			if(_txLineList.length > 0)
				txLine = _txLineList.getItemAt(0) as TxLine;
			
			return txLine;
		}
		
		public function lastTxLine():TxLine
		{
			var txLine:TxLine =  null;
			if(_txLineList.length > 0)
				txLine = _txLineList.getItemAt(_txLineList.length -1) as TxLine;
			
			return txLine;
		}
		
		public function firstInvalidTxLine():TxLine
		{
			var txLine:TxLine = null;
			var firstInvalidTextLine:TextLine;
			firstInvalidTextLine = _textBlock.firstInvalidLine;
			if(firstInvalidTextLine == null)
				return null;
			
			txLine = this.getTxLineAtCharIndex(firstInvalidTextLine.textBlockBeginIndex);
			return txLine;
		}
		
		public function releaseTxLines(firstTxLine:TxLine, lastTxLine:TxLine):ArrayCollection
		{
			var releasedTextLines:ArrayCollection = new ArrayCollection();
			var firstTextLine:TextLine = (firstTxLine.segments.getItemAt(0) as TxLineSegment).textLine;
			var lastTextLine:TextLine = (lastTxLine.segments.getItemAt(lastTxLine.segments.length - 1) as TxLineSegment).textLine;
			var firstTxIndex:int = _txLineList.getItemIndex(firstTxLine);
			var lastTxIndex:int = _txLineList.getItemIndex(lastTxLine);
			
			
			_textBlock.releaseLines(firstTextLine, lastTextLine);
			
			for(var i:int=firstTxIndex; i<=lastTxIndex; i++)
			{
				var txLine:TxLine = _txLineList.getItemAt(firstTxIndex) as TxLine;
				
				for(var j:int=0; j< txLine.segments.length; j++)
				{
					var textLine:TextLine = (txLine.segments.getItemAt(j) as TxLineSegment).textLine;
					releasedTextLines.addItem(textLine);
				}
				
				_txLineList.removeItemAt(firstTxIndex);
			}
			
			return releasedTextLines;
		}
		
		public function toString():String
		{
			return _id;
		}
		
		public function get format():ICharacterFormat
		{
			return this._format;
		}
		
		public function getTextRunByAbsolute(position:int):TxtRun {
			var el:RunElement;
			for(var i:int = 0, len:int = _runList.length; i< len;i++)
			{
				el = getRun(i);
				if(el.type=="TxtRun")				
				{
					
					if(position >= el.absoluteStart && position <= el.absoluteStart + el.textLength)
					{
						return (el as TxtRun);
						break;
					} 
				}
			}
			return null;
		}

	}
}